home *** CD-ROM | disk | FTP | other *** search
/ Inside Mac Games Volume 4 #1 & #2 / IMG 34 JanFeb 1996.iso / Essentials / MGD4Codeƒ / GoodbyWorld1.c < prev    next >
Text File  |  1995-12-16  |  19KB  |  453 lines

  1. //==============================================================================================\\
  2. //        -----------------------------------------------------------------------------------        \\
  3. //        GoodbyWorld1.c version 1.0.0    copyright © 1993…1995 Jamie McCornack                    \\
  4. //        -----------------------------------------------------------------------------------        \\
  5. //         Demo program for Macintosh GameWriter 1.0.0, a training program…                        \\
  6. //        …for beginning Mac game programmers. MGW1 includes MGWExterns1.h, MGWUtilities1.c,…        \\
  7. //        …MGWSound1.c, MGWGraphics1.c, MGWGraphicsBWLite1.c, HelloWorld.rsrc and an assortment…    \\
  8. //        …of demo programs; projects HelloWorld1.π etc. and source code files HelloWorld1.c etc.    \\
  9. //         A tutorial is available in Tricks of the Mac Game Programming Gurus, published…        \\
  10. //        …by Hayden Books, August 1995.                                                            \\
  11. //                                                                                                \\
  12. //        This code is offered by the copyright holders for no fee and for whatever use…            \\
  13. //        …you care to make of it, but we do hope you remember where it came from.                \\
  14. //                                                                                                \\
  15. //        Please send bug reports to MacGameDev at America OnLine.    macgamedev@aol.com            \\
  16. //        Suggestions and observations are also appreciated.                                        \\
  17. //        Updates and upgrades will be available now and then from the above e-mail address.        \\
  18. //==============================================================================================\\
  19.  
  20. // This program opens a window, displays an 8-bit color background, and at first…
  21. // …mouseclick, runs a clam across the screen. At second mouseclick, the clam stops,…
  22. // …talks, and flaps its face for 40 frames. Then it blows up, and the program quits.
  23.  
  24.  
  25. #include "MGWExterns1.h"
  26.  
  27. #define     kPutInFront    (WindowPtr)-1L
  28. #define     kWaitTicks     4L    // Sets the delay in Ticks between frames. Try 3. Try 2. Try 0.
  29. #define     kStepLength    14    // Sets the distance in pixels between sprite moves.
  30. #define  kFrontFace        0
  31. #define  kBlinkFace     1
  32. #define  kEehFace        2
  33. #define  kOohFace        3
  34. #define  kStepRightFace    4
  35. #define  kWalkRightFace    5
  36. #define  kRunRightFace    6
  37. #define  kMaxClamFaces    7
  38. #define  kBoomFace0        0    // Couldn't resist the onomonopea.
  39. #define  kBoomFace1        1
  40. #define  kBoomFace2        2
  41. #define  kBoomFace3        3
  42. #define  kBoomFace4        4
  43. #define  kBoomFace5        5
  44. #define  kBoomFace6        6
  45. #define  kBoomFace7        7
  46. #define  kMaxFireFaces    8
  47. #define  kShrinkFactor    -16        // -kShrinkFactor expands the explosion rect from 32x32.
  48.                                 // For optimum size (64x64), kShrinkFactor = -16. Try -48. Try 0.
  49.                                 // But remember: any scaling slows down CopyBits.
  50.                 // The resource constants--with 'r' prefix like Apple wants them.}
  51. #define  rClamFacesID    136        // The 'PICT' ID# where the views of the clam are located.}
  52. #define  rClamMasksID    130        // The 'PICT' ID# where the clam masks are located.}
  53. #define  rFireFacesID    141        // 141 for cartoon version, 142 for photo version.}
  54. #define  rFireMasksID    140        // The 'PICT' ID# where the explosion masks are located.}
  55. #define  rBackgroundID    134        // The 'PICT' ID# where the background picture is located.}
  56. #define  rMainWindowID    128        // The 'WIND' resource ID# for the main window.}
  57.  
  58. #define  rGoodbyeSndID    3000
  59. #define  rFootstepSndID    3001
  60. #define  rImpactSndID    3002
  61. #define  rDizzySndID    3003
  62. #define  rExplodeSndID    3004
  63.  
  64. #define  kColorBitsPreferred    8
  65.     
  66. typedef struct
  67. {
  68.     Rect    face;
  69.     Rect    mask;
  70. } tSpriteType;
  71.  
  72.  
  73. Rect    bigPictureRect, fireFacesRect, fireMasksRect,fireIsAtRect,
  74.         clamFacesRect, clamMasksRect, clamIsAtRect, clamWasAtRect, clamComboRect;
  75. CGrafPtr    workCPort, clamFacesCPort, fireFacesCPort, backgroundCPort;
  76. GrafPtr mainWindow, clamMasksPort, fireMasksPort;
  77. Boolean    itWorked;
  78. long    targetTick;
  79. short        thisSprite, thisFaceCounter;
  80.  
  81. tSpriteType    clamSprite[kMaxClamFaces], fireSprite[kMaxFireFaces];
  82.  
  83. extern    Boolean        gUserWantsSound;
  84.     
  85.     
  86. //==============================================================  Prototypes
  87.  
  88. void InitAll(void);
  89. void OpenMainWindow (void);
  90. void SetTheRects(void);
  91. void SetTheCPorts(void);
  92. void ShowClam (void);
  93. void DoDelay (void);
  94. void RunRight (void);
  95. void LipSynch (void);
  96. void ShowExplosion (void);
  97. void Explode (void);
  98. void SayGoodbye (void);
  99.  
  100. //==============================================================  Functions
  101.  
  102. //--------------------------------------------------------------  InitAll
  103.  
  104. void InitAll(void)
  105. {
  106.     InitToolbox();
  107.     if (WhatsOurDepth() < kColorBitsPreferred)    // Compare color depth with what we want.
  108.         YellowAlert(kPref8BitColor);            // If smaller, notify user.
  109.     if (WhatsOurDepth() > kColorBitsPreferred)    // Compare color depth with what we want.
  110.         YellowAlert(kPrefDownTo8BitColor);        // If larger, notify user.
  111.     gUserWantsSound = TRUE;
  112.     InitializeForSound();
  113.     SetTheRects();        // Since some of these Rects define fields in CGrafPorts,…
  114.     SetTheCPorts();        // …set the rects before setting the ports.
  115.     thisSprite = kFrontFace;
  116.     thisFaceCounter = 0;
  117.     targetTick = TickCount() + kWaitTicks;
  118.     HideCursor();        // This demo doesn't use mouse input, so why look at it?
  119. }
  120.  
  121. //--------------------------------------------------------------  OpenMainWindow
  122.  
  123. void OpenMainWindow (void)
  124. {
  125.     mainWindow = GetNewCWindow(128, 0L, kPutInFront);    // Load window from resource.
  126.     ShowWindow((GrafPtr)mainWindow);                    // Now display it.
  127.     SetPort((GrafPtr)mainWindow);                        // Make its port current.
  128.     ClipRect(&bigPictureRect);                            // Set its clip region.
  129.     CopyRgn(mainWindow->clipRgn, mainWindow->visRgn);    // Set its visRgn.
  130.     ForeColor(blackColor);                                // Set its pen color to black.
  131.     BackColor(whiteColor);                                // Set background color white.
  132. }
  133.  
  134. //--------------------------------------------------------------  SetTheRects
  135.  
  136. void SetTheRects(void)    // The most tedious part of programming this type of game.
  137. {
  138.     SetRect(&clamFacesRect, 0, 0, 448, 64);        // Size and shape of PixMap for the clam faces.
  139.     SetRect(&clamMasksRect, 0, 0, 448, 32);        // Size and shape of BitMap for the clam masks.
  140.     SetRect(&bigPictureRect, 0, 0, 512, 322);    // The shape of the picture we'll put in the main window, workCPort and backgroundCPort.}
  141.     SetRect(&clamIsAtRect, 200, 244, 232, 276);    // The shape (32 x 32) of the images of Clem, and the position of the first image.}
  142.     SetRect(&fireFacesRect, 0, 0, 192, 192);    // Size and shape of PixMap for the fire faces.
  143.     SetRect(&fireMasksRect, 0, 0, 192, 192);    // Size and shape of PixMap for the fire masks.
  144.     clamWasAtRect = clamIsAtRect;                // Initializing clamIsAtRect...it has to start somewhere, and this is handy.}
  145.     clamComboRect = clamIsAtRect;
  146.     
  147.         // And now, the tedium. In this sample, all we're doing is showing the clam running across the screen to the right.}
  148.         // However, if you use ResEdit and look at 'PICT' 129 in Sample.rsrc, you'll find 28 different views of the clam.}
  149.         // If we wanted the clam to run left too, and walk slowly, and face the user, and blink its eyes, we'd be calling…}
  150.         // …SetRect 56 times--one face and one mask per sprite. And if we had jumping clams and rear views of clams…}
  151.         // …and starfish and clamdiggers and other hazards of the clam environment, we might have hundreds of rects to set.}
  152.     SetRect(&clamSprite[kFrontFace].face, 320, 32, 352, 64);    // The shape and position of sprite[kFrontFace].face on clamFacesCPort.portPixMap.
  153.     SetRect(&clamSprite[kFrontFace].mask, 320, 0, 352, 32);    // The shape and position of sprite[kFrontFace].mask on masksPort.portPixMap.
  154.     SetRect(&clamSprite[kBlinkFace].face, 320, 0, 352, 32);    // Note that some faces (e.g. eyes open or closed) use the same mask,…
  155.     SetRect(&clamSprite[kBlinkFace].mask, 320, 0, 352, 32);    // …since they have the same silhouette.}
  156.     SetRect(&clamSprite[kEehFace].face, 352, 0, 384, 32);    // I could write more comments here, but setting these rects…
  157.     SetRect(&clamSprite[kEehFace].mask, 352, 0, 384, 32);    // …is already tedious enough without a bunch of busy-work.
  158.     SetRect(&clamSprite[kOohFace].face, 352, 32, 384, 64);
  159.     SetRect(&clamSprite[kOohFace].mask, 352, 0, 384, 32);    
  160.     SetRect(&clamSprite[kStepRightFace].face, 192, 0, 224, 32);    
  161.     SetRect(&clamSprite[kStepRightFace].mask, 192, 0, 224, 32);    
  162.     SetRect(&clamSprite[kWalkRightFace].face, 224, 0, 256, 32);    
  163.     SetRect(&clamSprite[kWalkRightFace].mask, 224, 0, 256, 32);    
  164.     SetRect(&clamSprite[kRunRightFace].face, 160, 0, 192, 32);    // BTW, there are plenty more clam faces and masks in the 'PICT's,…
  165.     SetRect(&clamSprite[kRunRightFace].mask, 160, 0, 192, 32);    // …if you feel you need rect setting practice.  :-)
  166.     SetRect(&fireSprite[kBoomFace0].face, 0, 0, 64, 64);
  167.     SetRect(&fireSprite[kBoomFace0].mask, 0, 0, 64, 64);
  168.     SetRect(&fireSprite[kBoomFace1].face, 64, 0, 128, 64);
  169.     SetRect(&fireSprite[kBoomFace1].mask, 64, 0, 128, 64);
  170.     SetRect(&fireSprite[kBoomFace2].face, 128, 0, 192, 64);
  171.     SetRect(&fireSprite[kBoomFace2].mask, 128, 0, 192, 64);
  172.     SetRect(&fireSprite[kBoomFace3].face, 0, 64, 64, 128);
  173.     SetRect(&fireSprite[kBoomFace3].mask, 0, 64, 64, 128);
  174.     SetRect(&fireSprite[kBoomFace4].face, 64, 64, 128, 128);
  175.     SetRect(&fireSprite[kBoomFace4].mask, 64, 64, 128, 128);
  176.     SetRect(&fireSprite[kBoomFace5].face, 128, 64, 192, 128);
  177.     SetRect(&fireSprite[kBoomFace5].mask, 128, 64, 192, 128);
  178.     SetRect(&fireSprite[kBoomFace6].face, 0, 128, 64, 192);    
  179.     SetRect(&fireSprite[kBoomFace6].mask, 0, 128, 64, 192);
  180.     SetRect(&fireSprite[kBoomFace7].face, 64, 128, 128, 192);    
  181.     SetRect(&fireSprite[kBoomFace7].mask, 64, 128, 128, 192);    
  182. }
  183.  
  184. //--------------------------------------------------------------  SetTheCPorts
  185.  
  186. void SetTheCPorts(void)    // Create the CGrafPorts and load their .portPixMap fields.
  187. {
  188.             // Create BitMap for clam masks. NOTE THIS IS A BITMAP!
  189.     CreateOffScreenBitMap (&clamMasksRect, &clamMasksPort);
  190.     LoadGraphic (rClamMasksID);        // …load 'PICT' resource for the clam masks.
  191.     
  192.             // Create BitMap for fire masks. NOTE THIS IS A BITMAP!
  193.     CreateOffScreenBitMap (&fireMasksRect, &fireMasksPort);
  194.     LoadGraphic (rFireMasksID);        // …load 'PICT' resource for the fire masks.
  195.     
  196.             // Create PixMap for clam faces.
  197.     CreateOffScreenPixMap (&clamFacesRect, &clamFacesCPort);
  198.     LoadGraphic (rClamFacesID);        // …load 'PICT' resource for the clam faces.
  199.  
  200.             // Create PixMap for fire faces.
  201.     CreateOffScreenPixMap (&fireFacesRect, &fireFacesCPort);
  202.     LoadGraphic (rFireFacesID);        // …load 'PICT' resource for the fire faces.
  203.  
  204.             // Create PixMap for the background.
  205.     CreateOffScreenPixMap (&bigPictureRect, &backgroundCPort);
  206.     LoadGraphic(rBackgroundID);    // …load 'PICT' resource for the background picture.
  207.     
  208.             // Create PixMap for offscreen graphics work.
  209.     CreateOffScreenPixMap (&bigPictureRect, &workCPort);
  210.  
  211.     OpenMainWindow();
  212.  
  213.         //{This fills the main window with the background picture, so the user can see it.
  214.     CopyBits(&((GrafPtr)backgroundCPort)->portBits,
  215.         &((GrafPtr)mainWindow)->portBits, 
  216.         &bigPictureRect, &bigPictureRect, srcCopy, mainWindow->visRgn);
  217.  
  218. // This fills the workCPort.portPixMap with the background picture, so updates can be done quickly.
  219.     CopyBits(&((GrafPtr)backgroundCPort)->portBits,
  220.         &((GrafPtr)workCPort)->portBits, 
  221.         &bigPictureRect, &bigPictureRect, srcCopy, mainWindow->visRgn);
  222. }
  223.  
  224. //--------------------------------------------------------------  ShowClam 
  225.  
  226. void ShowClam (void)    // Do the animation and make it appear on the screen.
  227. {
  228.     CopyMask(&((GrafPtr)clamFacesCPort)->portBits, 
  229.         &((GrafPtr)clamMasksPort)->portBits, 
  230.         &((GrafPtr)workCPort)->portBits, 
  231.         &clamSprite[thisSprite].face, 
  232.         &clamSprite[thisSprite].mask, 
  233.         &clamIsAtRect);
  234. // Now there is an image of a clam in the new position in workMap.  If we had done this work in…
  235. // mainWindow, we would have seen considerable flickering.  Instead, we did it offscreen, and left the…
  236. // previous image of the clam visible on the screen while we worked.
  237.  
  238.     UnionRect(&clamWasAtRect, &clamIsAtRect, &clamComboRect);
  239. // Find the smallest rectangle which will cover the old position of the clam and the new.
  240.  
  241.     CopyBits(&((GrafPtr)workCPort)->portBits, 
  242.         &(((GrafPtr)mainWindow)->portBits), 
  243.         &clamComboRect, &clamComboRect, srcCopy, mainWindow->visRgn);
  244. // Copy the contents of comboRect from workCPort->portPixMap to the main window. In one swell foop, the old clam…}
  245. //…will be erased, and the new clam overlayed onto the background picture. Wallah! Flicker-free animation!}
  246.  
  247.     CopyBits(&((GrafPtr)backgroundCPort)->portBits, 
  248.         &(((GrafPtr)workCPort)->portBits), 
  249.         &clamIsAtRect, &clamIsAtRect, srcCopy, mainWindow->visRgn);
  250. // Restore the workCPort by covering up our clam with the background it obscures.
  251. // This way, workCPort->portPixMap is identical to backgroundCPort->portPixMap,…
  252. // without having to copy the entire PixMap.
  253. }
  254.  
  255. //--------------------------------------------------------------  ShowExplosion 
  256.  
  257. void ShowExplosion (void)    // Much like ShowClams().
  258. {
  259.     CopyMask(&((GrafPtr)fireFacesCPort)->portBits, 
  260.         &((GrafPtr)fireMasksPort)->portBits, 
  261.         &((GrafPtr)workCPort)->portBits, 
  262.         &fireSprite[thisSprite].face, 
  263.         &fireSprite[thisSprite].mask, 
  264.         &fireIsAtRect);
  265.  
  266. //    Don't need UnionRect() here because fireIsAt rect doesn't move.
  267.  
  268.     CopyBits(&((GrafPtr)workCPort)->portBits, 
  269.         &(((GrafPtr)mainWindow)->portBits), 
  270.         &fireIsAtRect, &fireIsAtRect, srcCopy, mainWindow->visRgn);
  271.  
  272.     CopyBits(&((GrafPtr)backgroundCPort)->portBits, 
  273.         &(((GrafPtr)workCPort)->portBits), 
  274.         &fireIsAtRect, &fireIsAtRect, srcCopy, mainWindow->visRgn);
  275. }
  276.  
  277. //--------------------------------------------------------------  DoDelay
  278.  
  279. // This is the companion function to the above function (LogNextTick()).
  280. // We do nothing but loop until TickCount() catches up with (or passes) our…
  281. // global variable tickNext.
  282.  
  283. void DoDelay (void)
  284. {
  285.     do
  286.     {
  287.     }
  288.     while (TickCount() < targetTick);            // Loop until TickCount() catches up.
  289.     targetTick = TickCount() + kWaitTicks;
  290. }
  291. //--------------------------------------------------------------  RunRight
  292.  
  293. void RunRight (void)    //Gives a sequence of views of the clam running to the right.
  294. {
  295.     switch    (thisSprite)                    //If the current view of the clam is…
  296.     {    case    (kStepRightFace):             // …kStepRightFace, then set thisSprite to…
  297.         {    thisSprite = kWalkRightFace;    // …kWalkRightFace, and if it is currently…
  298.             break;    }
  299.         case    (kWalkRightFace):             // …kWalkFace, then set it to…
  300.         {    thisSprite = kRunRightFace;        // …kRunRightFace.
  301.             PlayASound(rFootstepSndID, kHighestSoundPriority);    // Note sound priority.
  302.             break;    }
  303.     // And if it was neither kStepRightFace nor kWalkRightFace, then thisSprite was either…
  304.         default                    :             // …kRunRightFace, or what it was when RunRight()…
  305.             thisSprite = kStepRightFace;    // …was called, so we set it to kStepRightFace
  306.     }
  307.     clamWasAtRect = clamIsAtRect;            // Store the clam's current position as its last position,…
  308.                                             // …we'll be erasing it next time through the loop.
  309.     OffsetRect(&clamIsAtRect, kStepLength, 0);    // Set the clam's next position--it'll be…
  310.                                             // …kStepLength pixels to the right of its last position.
  311.     if (clamIsAtRect.left > 512)                // If the clam has wandered out of sight,…
  312.     {                                        // …set the right border of clamIsAtRect…
  313.         clamIsAtRect.right = 0;                // …to the left edge of the screen…
  314.         clamIsAtRect.left = -32;            // …and move the left border of clamIsAtRect…
  315.     }                                        // …as needed to maintain its 32 x 32 shape & size.
  316.   ShowClam();                        // The actual animation routine!
  317.   DoDelay();    // Do nothing for a while. In a real game, you'll want to use time more wisely.
  318. }
  319.  
  320. //--------------------------------------------------------------  LipSynch
  321.  
  322. // This routine has the clam stop moving, face the screen, and move its face.
  323. // Note there are diferences in sequence between this routine for "Goodbye, World,"…
  324. // …and the "Hello, World," versions, since the different words require different moves.
  325.  
  326. void LipSynch (void)
  327. {
  328.     while (thisFaceCounter < 40)
  329.     {
  330.         thisFaceCounter = thisFaceCounter + 1;
  331.          switch (thisFaceCounter)
  332.          {    case    (1): 
  333.             {    thisSprite = kFrontFace;
  334.                 break;    }
  335.             case    (2): 
  336.             {    thisSprite = kEehFace;
  337.                 break;    }
  338.             case    (4): 
  339.             {    thisSprite = kFrontFace;
  340.                 break;    }
  341.             case    (5): 
  342.             {    thisSprite = kOohFace;
  343.                 break;    }
  344.             case    (9): 
  345.             {    thisSprite = kEehFace;
  346.                 break;    }
  347.             case    (11): 
  348.             {    thisSprite = kFrontFace;
  349.                 break;    }
  350.             case    (12): 
  351.             {    thisSprite = kBlinkFace;
  352.                 break;    }
  353.             case    (14): 
  354.             {    thisSprite = kOohFace;
  355.                 break;    }
  356.             case    (17): 
  357.             {    thisSprite = kFrontFace;
  358.                 break;    }
  359.             case    (35): 
  360.             {    thisSprite = kBlinkFace;
  361.                 break;    }
  362.             case    (38): 
  363.             {    thisSprite = kFrontFace;
  364.                 break;    }
  365.         }
  366.         ShowClam();
  367.         DoDelay();        // Do nothing for a while, so the lips don't flap too fast.
  368.     }
  369. }
  370.  
  371. //--------------------------------------------------------------  Explode
  372.  
  373. // This routine sequences the explosion images.
  374.  
  375. void Explode (void)
  376. {
  377.     while (thisFaceCounter < 40)
  378.     {
  379.         thisFaceCounter++;    // Might as well make it look like a C program. Increments thisFaceCounter.
  380.          switch (thisFaceCounter)
  381.          {
  382.              case    (0): 
  383.             {    thisSprite = kBoomFace0;
  384.                 break;    }
  385.              case    (1): 
  386.             {    thisSprite = kBoomFace1;
  387.                 break;    }
  388.             case    (2): 
  389.             {    thisSprite = kBoomFace2;
  390.                 break;    }
  391.             case    (3): 
  392.             {    thisSprite = kBoomFace3;
  393.                 break;    }
  394.             case    (4): 
  395.             {    thisSprite = kBoomFace4;
  396.                 break;    }
  397.             case    (5): 
  398.             {    thisSprite = kBoomFace5;
  399.                 break;    }
  400.             case    (6): 
  401.             {    thisSprite = kBoomFace6;
  402.                 break;    }
  403.             case    (7): 
  404.             {    thisSprite = kBoomFace7;    // kBoomFace7 is blank, BTW.
  405.                 break;    }        
  406.         }
  407.         ShowExplosion();
  408.         DoDelay();        // Do nothing for a while, so the frame rate isn't too high.
  409.     }
  410. }
  411.  
  412. //--------------------------------------------------------------  SayGoodbye
  413.  
  414. // This routine sequences the explosion images.
  415.  
  416. void SayGoodbye (void)
  417. {
  418.     PlayASound(rGoodbyeSndID, kMediumSoundPriority);
  419.     LipSynch();
  420.     thisFaceCounter = -1;        // Explode() will increment this to zero in its first loop.
  421.     fireIsAtRect = clamIsAtRect;    // Locates the explosion at the clam's final position.
  422.     InsetRect(&fireIsAtRect,kShrinkFactor,kShrinkFactor);    // Negative numbers "outset" rect.
  423.     PlayASound(rExplodeSndID, kMediumSoundPriority);
  424.     Explode();
  425. }
  426.  
  427. //--------------------------------------------------------------  main
  428. //----------------------------------------------------------------------
  429.  
  430. void main(void)
  431. {
  432.     InitAll();
  433.     while (!Button())    // Before the user presses the mouse button, nothing happens.
  434.         {
  435.         }
  436.     while (Button())    // When the user presses the mouse button, nothing happens.
  437.         {
  438.         }
  439.     while (!Button())    // When the mouse button is released, continue to…
  440.         {
  441.         RunRight();        // …show the clam running right (duh),…
  442.         }
  443.     while (Button())    // …until the button is pushed.
  444.         {                // Do nothing until the button is released.
  445.         }
  446.     SayGoodbye();
  447.     CloseDownSound();
  448. }                        // And we're done.
  449.  
  450. //------------------------------------------------------------------------------------------\\
  451. //                                    End HelloWorld1.c                                        \\
  452. //------------------------------------------------------------------------------------------\\
  453.